#include <QCoreApplication>
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QFile>
#include <QDir>

#include "../config/config.h"
#include "../util/Singleton.h"
#include "DbManager.h"

X_BEGIN_NAMESPACE

const char DbManager::DB_FILE[] = "xchat.db";
const char DbManager::SCHEMA_INIT_FILE[] = "schema/schema.init.sql";
const char DbManager::SCHEMA_UPDATE_FILE[] = "schema/schema.update.sql";

DbManager::DbManager(QObject *parent)
    : QObject(parent)
{
}

DbManager &DbManager::instance()
{
    return *Singleton<DbManager>::instance(DbManager::createInstance);
}

QString DbManager::dbFile(const QString bareJid)
{
    return Config::instance().storagePath(bareJid) + DB_FILE;
}

QString DbManager::schemaInitFile()
{
    QString initFile = Config::instance().resourcesPath() + SCHEMA_INIT_FILE;
    return initFile;
}

QString DbManager::schemaUpdateFile()
{
    QString updateFile = Config::instance().resourcesPath() + SCHEMA_UPDATE_FILE;
    return updateFile;
}

/**
 * @brief 准备数据。
 */
bool DbManager::prepareData(const QString& bareJid)
{
    // 检查数据定义文件是否存在
    if(!QFile::exists(schemaInitFile())){
        QMessageBox::critical(NULL, "医疗云通讯", "数据定义文件不存在，程序即将退出.", QMessageBox::Ok);
        return false;
    }

    // 检查数据文件存不存在
    if(!checkDataFile(dbFile(bareJid))){
        QMessageBox::critical(NULL, "医疗云通讯", "数据文件检查失败，程序即将退出.", QMessageBox::Ok);
        return false;
    }

    // 升级脚本
    if(!updateDatabase()){
        QMessageBox::critical(NULL, "医疗云通讯", "数据文件升级失败，程序即将退出.", QMessageBox::Ok);
        return false;
    }

    return true;
}

/**
 * @brief 检查数据文件，如果不存在创建之。
 * @param schemaFile
 * @return
 */
bool DbManager::checkDataFile(const QString &dbFileName)
{
    if(!QFile::exists(dbFileName))
    {
        QDir fileDir = QFileInfo(dbFileName).absoluteDir();
        QString strFileDir = QFileInfo(dbFileName).absolutePath();
        if(!fileDir.exists())
        {
            fileDir.mkpath(strFileDir);
        }

        QFile dbFile(dbFileName);
        if(!dbFile.open(QIODevice::WriteOnly))//未成功打开
        {
            dbFile.close();
            return false;
        }
        dbFile.close();

        // 创建之后，初始化数据库
        initDatabase(dbFileName);
    }

    // 打开数据文件
    openDatabase(dbFileName);

    // 升级
    updateDatabase();

    return true;
}

/**
 * @brief 打开数据库文件
 *
 * @param dbFileName
 * @return 成功打开返回true
 */
bool DbManager::openDatabase(const QString &dbFileName)
{
    if(m_db.isOpen()) return true;

    m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setDatabaseName(dbFileName);

    return m_db.open();
}

/**
 * @brief 关闭数据库
 */
void DbManager::closeDataBase()
{
    m_db.close();
}

/**
 * @brief 初始化数据库文件。
 *
 * @return
 */
bool DbManager::initDatabase(const QString& dbFileName)
{
    openDatabase(dbFileName);

    // 使用初始化脚本创建数据库
    QFile initFile(schemaInitFile());
    if(!initFile.exists()){
        return false;
    }

    initFile.open(QFile::ReadOnly);

    QSqlQuery query(m_db);
    executeQueriesFromFile(initFile, query);

    initFile.close();

    return true;
}

/**
 * @brief 升级数据库文件。
 *
 * @return
 */
bool DbManager::updateDatabase()
{
    QFile updateFile(schemaUpdateFile());
    if(!updateFile.exists()){
        return false;
    }

    updateFile.open(QFile::ReadOnly);

    QSqlQuery query(m_db);
    executeQueriesFromFile(updateFile, query);

    updateFile.close();

    return true;
}

/**
 * @brief executeQueriesFromFile Read each line from a .sql QFile
 * (assumed to not have been opened before this function), and when ; is reached, execute
 * the SQL gathered until then on the query object. Then do this until a COMMIT SQL
 * statement is found. In other words, this function assumes each file is a single
 * SQL transaction, ending with a COMMIT line.
 *
 * Copied from https://www.fluxitek.fi/2013/10/reading-sql-text-file-sqlite-database-qt/
 */
void DbManager::executeQueriesFromFile(QFile &file, QSqlQuery &query)
{
    while (!file.atEnd()){
        QByteArray readLine="";
        QString cleanedLine;
        QString line="";

        bool finished=false;
        while(!finished && !file.atEnd()){
            readLine = file.readLine();
            cleanedLine = readLine.trimmed();

            // 删除行尾的注释文本
            QStringList strings=cleanedLine.split("--");
            cleanedLine=strings.at(0);

            // 删除以注释开头的行与DROP行
            if(!cleanedLine.startsWith("--")
                    && !cleanedLine.startsWith("DROP")
                    && !cleanedLine.isEmpty()){
                line += cleanedLine;
            }

            if(cleanedLine.endsWith(";")){
                break;
            }

            if(cleanedLine.startsWith("COMMIT")){
                finished=true;
            }
        }

        if(!line.isEmpty()){
            query.exec(line);
        }

        if(!query.isActive()){
            if(query.lastError().isValid()) qDebug() << query.lastError();

            //qDebug() << "Executed query:"<< query.executedQuery();
            //qDebug() << "Last query:"<< query.lastQuery();
        }
    }
}

QSqlQuery DbManager::createQuery()
{
    return QSqlQuery(m_db);
}

QSqlQuery DbManager::createQuery(const QString &sql)
{
    return QSqlQuery(sql, m_db);
}

/**
 * @brief 单件工厂
 *
 * @return
 */
DbManager *DbManager::createInstance()
{
    return new DbManager();
}

X_END_NAMESPACE
